package com.study.common.util;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Optional;

/**
 * @ClassName: DesensitizedUtils
 * @Description (数据脱敏依赖util,通过反射调用)
 * @Date:2019/10/31 20:40
 * @Author: lk
 * @see
 * @since JDK 1.8
 */
public class DesensitizedUtils {

    /**
     * 除前面几位和后面几位外，其他的字符以星号代替，如360622********6640
     * 性能比正则写法提高60多倍，比字符串替换提高8倍多，并且运行次数增加性能差异越大
     * @param content 传入的字符串
     * @param frontNum 保留前面字符的位数
     * @param endNum 保留后面字符的位数
     * @return 带星号的字符串
     */
    public static String replaceString1(String content, int frontNum, int endNum) {
        if (StringUtils.isBlank(content)) {
            return "";
        }
        int len = content.length();
        if (frontNum >= len || frontNum < 0 || endNum >= len || endNum < 0)
            return content;
        if (frontNum + endNum >= len)
            return content;
        int beginIndex = frontNum;
        int endIndex = len - endNum;
        char[] cardChar = content.toCharArray();
        for (int j = beginIndex; j < endIndex; j++) {
            cardChar[j] = '*';
        }
        return new String(cardChar);
    }

    /**
     * @description 只对指定的前几位进行脱敏 如：*宫问天
     * @author hw
     * @date 2023/9/12 15:45:49
     * @param content: 内容
     * @param frontNum: 要脱敏的位数
     * @return:
     */
    public static String replaceString2(String content, int frontNum) {
        if (StringUtils.isBlank(content)) {
            return "";
        }
        char[] cardChar = content.toCharArray();
        for (int j = 0; j < content.length() && j < frontNum ; j++) {
            //换成*号
            cardChar[j] = '*';
        }
        return new String(cardChar);
    }


    /**
     * @description 只保留前面字符的位数，对后面的进行脱敏，如G362421************
     * @author hw
     * @date 2023/9/12 15:45:49
     * @param content: 内容
     * @param frontNum: 要保留的位数
     * @return:
     */
    public static String replaceString3(String content, int frontNum) {
        if (StringUtils.isBlank(content)) {
            return "";
        }
        char[] cardChar = content.toCharArray();
        for (int j = frontNum; j < content.length() ; j++) {
            //换成*号
            cardChar[j] = '*';
        }
        return new String(cardChar);
    }

    /**
     * @description 对倒数第6位-倒数第9位脱敏 如： 江西省南昌市青云谱区新溪桥东一路****元501号
     * @author hw
     * @date 2023/9/12 15:45:49
     * @param content: 内容
     * @return:
     */
    public static String replaceString4(String content) {
        if (StringUtils.isBlank(content)) {
            return "";
        }
        if(content.length() < 5){
            return content;
        }
        int beginIndex;
        char[] cardChar = content.toCharArray();
        if(content.length() < 9){
            //小于9位，对倒数第五位前面的都脱敏
            for (int j = 0; j < content.length()-5  ; j++) {
                //换成*号
                cardChar[j] = '*';
            }
        }else {
            //大于9位，对倒数第五位-倒数第9位都脱敏
            beginIndex = content.length() - 9;
            for (int j = beginIndex; j < content.length()-5  ; j++) {
                //换成*号
                cardChar[j] = '*';
            }
        }
        return new String(cardChar);
    }

    /**
     * @description 替换部分字符为*，如身份证号：前六位，后四位，其他用星号隐藏每位1个星号，比如：6222600**********1234
     * @author hw
     * @date 2023/9/12 17:45:08
     * @param cardNum:
     * @return:
     */
    public static String bankCard(String cardNum) {
        return replaceString1(cardNum, 6, 4);
    }
    
    /**
     * @description 对学籍号进行脱敏，只保留前面7位，后面的用*代替，如：G362421************
     * @author hw
     * @date 2023/9/12 17:45:23
     * @param number:
     * @return:
     */
    public static String front7(String number) {
    	return replaceString3(number, 7);
    }

    /**
     * @description 对日期今夕脱敏，保留前8位，后面的用*代替，如：2024-01-**
     * @author hw
     * @date 2024/1/23 14:14:52
     * @param number:
     * @return:
     */
    public static String front8(String number) {
        return replaceString3(number, 8);
    }

    /**
     * @description 对年月日进行脱敏，只保留前面4位，后面的用*代替，如：2008******
     * @author hw
     * @date 2023/9/12 17:45:23
     * @param number:
     * @return:
     */
    public static String front4(String number) {
        return replaceString3(number, 4);
    }

    /**
     * @description 对所有字符都用*代替，如：******
     * @author hw
     * @date 2023/9/12 17:45:23
     * @param number:
     * @return:
     */
    public static String all(String number) {
        return replaceString3(number, 0);
    }

    /**
     * @description 对姓名进行脱敏，只脱敏前面1位 如：*宫问天
     * @author hw
     * @date 2023/9/12 17:45:23
     * @param name:
     * @return:
     */
    public static String name(String name) {
        return replaceString2(name, 1);
    }

    /**
     * @description 对倒数第6位-倒数第9位脱敏 如：江西省南昌市青云谱区新溪桥东一路****元501号
     * @author hw
     * @date 2023/9/12 17:45:23
     * @param address:
     * @return:
     */
    public static String address(String address) {
        return replaceString4(address);
    }

    /**
     * @description 金融资产脱敏规则
     * @author hw
     * @date 2023/9/12 19:07:32
     * @param balance:
     * @return:
     */
    public static String balance(Double balance) {
        String current = "0";
        if(balance==0) {
            current = "0";
        }else if(balance>0 && balance<=30000) {
            current = "3万以下";
        }else if(balance>30000 && balance<=100000) {
            current = DesensitizedUtils.getNumberReplaceRange(balance+"");
        }else if(balance>100000 && balance<=200000) {
            current = "10—20（含）万元";
        }else if(balance>200000 && balance<=500000) {
            current = "20—50（含）万元";
        }else if(balance>500000 && balance<=1000000) {
            current = "50—100（含）万元";
        }else if(balance>1000000) {
            current = "100万元以上";
        }
        return current;
    }

    //定义金额区间范围
    private static final Double INTERVAL = Double.parseDouble("50000");

    /**
     * @description 新的脱敏规则
     * @author hw
     * @date 2024/11/18 19:22
     * @param balance:
     * @return:
     */
    public static String balanceNew(Double balance) {
        //判空
        if(balance==0) {
            return "0元";
        }
        //上限为100万元
        if(balance>1000000) {
            return "100万元以上";
        }
        //向上取整获取上界区间值
        int upper = (int) (Math.ceil(balance/INTERVAL)*INTERVAL);
        //下界就是上界减去区间值
        int lower = (int) (upper - INTERVAL);
        //转换为万元单位
        String result = String.format("%d-%d（含）万元", lower/10000, upper/10000);
        //返回
        return result;
    }


    /**
     * @description 工商注册资金脱敏规则，以万为单位
     * @author hw
     * @date 2023/9/12 19:07:32
     * @param balanceString:
     * @return:
     */
    public static String balanceBusiness(String balanceString) {
        Double balance = Double.parseDouble(balanceString);
        String current = "3万以下";
        if(balance==0) {
            current = "0";
        }else if(balance>0 && balance<=3) {
            current = "3万以下";
        }else if(balance>3 && balance<=10) {
            current = DesensitizedUtils.getNumberReplaceRange(balance+"");
        }else if(balance>10 && balance<=20) {
            current = "10—20（含）万元";
        }else if(balance>20 && balance<=50) {
            current = "20—50（含）万元";
        }else if(balance>50 && balance<=100) {
            current = "50—100（含）万元";
        }else if(balance>100) {
            current = "100万元以上";
        }
        return current;
    }

    /**
     * @description 保留2位小数，不足2位则补0
     * 3123 -> 3123.00
     * 3123.1 -> 3123.10
     * 3123.12 -> 3123.12
     * @author hw
     * @date 2024/1/16 14:01:37
     * @param money: 
     * @return: 
     */
    public static String money(String money){
        try {
            //-不进行转换
            if(StrUtil.equals("-", money)){
                return money;
            }
            //保留2位小数
            double score = new BigDecimal(money).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            //不足两位则补0
            DecimalFormat decimalFormat = new DecimalFormat("0.00#");
            //转换返回
            return decimalFormat.format(score);
        }catch (Exception e){
            //非数字返回0.00
            return "0.00";
        }
    }

    public static void main(String[] args) throws Exception {
        /*System.out.println(getNumberReplaceRange(null));
        System.out.println(bankCard("360622197605186640"));
        System.out.println(bankCard("622260012354896541234"));
        System.out.println(front7("G362421199605061411"));
        System.out.println(name("南宫问天"));
        System.out.println(address("江西省"));
        System.out.println(address("江西省南昌"));
        System.out.println(address("江西省南昌市"));
        System.out.println(address("江西省南昌市青"));
        System.out.println(address("江西省南昌市青云"));
        System.out.println(address("江西省南昌市青云谱"));
        System.out.println(address("江西省南昌市青云谱区"));
        System.out.println(address("江西省南昌市青云谱区新溪桥东一路9区2单元501号"));
        System.out.println(money("3123"));
        System.out.println(money("3123.1"));
        System.out.println(money("3123.12"));
        System.out.println(money("3123.123"));
        System.out.println(money("null"));*/
        /*System.out.println(balanceNew(new Double(0)));
        System.out.println(balanceNew(new Double(12565)));
        System.out.println(balanceNew(new Double(60321)));
        System.out.println(balanceNew(new Double(110000)));
        System.out.println(balanceNew(new Double(200000)));
        System.out.println(balanceNew(new Double(230321)));
        System.out.println(balanceNew(new Double(280321)));
        System.out.println(balanceNew(new Double(60030021)));*/
        System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS")));
    }

    /**
     * @Description (具体细节隐藏,如患心脏病显示患病)
     * @author lk
     * @date 2019/11/3 12:09
     * @param blame
     * @return java.lang.String
     */
    public String bankBlame(String blame,String left,String right){
        if(StringUtils.isBlank(blame)){
            return "";
        }
        return StringUtils.left(blame, Integer.parseInt(left)).concat(StringUtils.right(blame, Integer.parseInt(right)));
    }
    /**
     * @Description (将数字替换范围如收入3200，显示两千至五千)
     * @author lk
     * @date 2019/11/3 12:16
     * @param number
     * @return java.lang.String
     */
    public String getNumberReplaceRange(String number,String minStr,String maxStr)throws Exception{
        String result="";
        double money = Double.parseDouble(number);
        double min = Double.parseDouble(minStr);
        double max = Double.parseDouble(maxStr);
        if(money<min){
            result=minStr+"以内";
        }else if(money<max){
            result=minStr+"至"+maxStr;
        }else{
            result="大于"+maxStr;
        }
        return result;
    }
    /**
     * Description (传递金额对金额进行脱敏 例如：金额为【352.66】，脱敏后为【3**.**】)
     * @author lk
     * @date 2020-12-19 11:16
     * @param param :
     * @return java.lang.String
     */
    public static String getNumberReplaceRange(String param){
        //先转成2位小数的格式
        param = money(param);
        StringBuilder res = new StringBuilder();
        String number = Optional.ofNullable(param).orElse("");
        int index = number.indexOf(".");
        int money = number.indexOf("元");
        for(int i=0;i < number.length();i++) {
            if(i==0 && i==index){
                res.append("0");
                res.append(number.charAt(i));
            }else if(i==0){
                res.append(number.charAt(i));
            }else if(i==index || i==money){
                res.append(number.charAt(i));
            }else{
                res.append("*");
            }
        }
        return res.toString();
    }

    /**
     * @Description (请用一句话描述方法的功能)
     * @author lk
     * @date 2019/12/5 21:45
     * @param json : 要脱敏的json对象
     * @param fieldRulsJson : 存储数据{"key","规则方法名"}  则表示key需要根据这个规则进行脱敏
     * @param keyMap : 首次调用传递null
     * @return com.alibaba.fastjson.JSONObject
     */
    public static JSONObject getJsonObj(JSONObject json,JSONObject fieldRulsJson,String keyMap) throws Exception{
        JSONObject jsonObj=new JSONObject(16);
        for(Map.Entry<String, Object> entry : json.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            jsonObj.put(key,value);
           // jsonObj.put(key,value);
            //判断数据类型是否是jsonObject
            if(value instanceof JSONObject){
                JSONObject resJson = getJsonObj(JSONObject.parseObject(String.valueOf(value)), fieldRulsJson, key);
                jsonObj.put(key,resJson);
            }else if(value instanceof JSONArray){
                JSONObject jsonArr = getJsonArr(JSONArray.parseArray(String.valueOf(value)), fieldRulsJson, key);
                jsonObj.put(key,jsonArr.get(key));
                //jsonObj.put(key,jsonArr);
            }else if(value instanceof String){
                for(Map.Entry<String, Object> entryField : fieldRulsJson.entrySet()) {
                    String field = entryField.getKey();
                    JSONObject rule = JSONObject.parseObject(JSON.toJSONString(entryField.getValue()));
                    if(field.equals(key)){
                        //调用方法传递规则和对应的字段
                        Object refDes = getRefDes(value,rule);
                        json.put(key,refDes);
                        jsonObj.put(key+"Mark",refDes);
                    }
                }
            }else if(value instanceof Integer){
                for(Map.Entry<String, Object> entryField : fieldRulsJson.entrySet()) {
                    String field = entryField.getKey();
                    JSONObject rule = JSONObject.parseObject(JSON.toJSONString(entryField.getValue()));
                    if(field.equals(key)){
                        //调用方法传递规则和对应的字段
                        Object refDes = getRefDes(value,rule);
                        json.put(key,refDes);
                        jsonObj.put(key+"Mark",refDes);
                    }
                }
            }else if(value instanceof Double){
                for(Map.Entry<String, Object> entryField : fieldRulsJson.entrySet()) {
                    String field = entryField.getKey();
                    JSONObject rule = JSONObject.parseObject(JSON.toJSONString(entryField.getValue()));
                    if(field.equals(key)){
                        //调用方法传递规则和对应的字段
                        Object refDes = getRefDes(value,rule);
                        json.put(key,refDes);
                        jsonObj.put(key+"Mark",refDes);
                    }
                }
            }
        }
        return jsonObj;
    }
    /**
     * @Description (传递数据和规则的方法名 进行脱敏)
     * @author lk
     * @date 2019/12/5 20:09
     * @param value : 数据
     * @param rule :  规则方法名
     * @return java.lang.Object
     */
    public static Object getRefDes(Object value,JSONObject rule) throws Exception{
        if(StringUtils.isBlank(String.valueOf(value))){
            return null;
        }
        DesensitizedUtils des = new DesensitizedUtils();
        Method method = des.getClass().getMethod(rule.getString("ruleMethod"), Class.forName("java.lang.String"),Class.forName("java.lang.String"),Class.forName("java.lang.String"));
        Object invoke = method.invoke(des, String.valueOf(value),rule.getString("ruleStart"),rule.getString("ruleEnd"));
        return invoke;
    }
    /**
     * @Description (传递json数组 判断是否为jsonobject类型，或者是否是json数组，一般为jsonobject)
     * @author lk
     * @date 2019/12/5 21:42
     * @param jsonArr : json数组 过来进行循环
     * @param fieldRulsJson : 存储数据{"key","规则方法名"}  则表示key需要根据这个规则进行脱敏
     * @param key :  key值
     * @return com.alibaba.fastjson.JSONObject
     */
    public static JSONObject getJsonArr(JSONArray jsonArr,JSONObject fieldRulsJson,String key) throws Exception {
        JSONObject jsonObj = new JSONObject();
        //用来存储
        JSONArray jsonArrs = new JSONArray(16);
        for (Object obj :jsonArr){
            if(obj instanceof JSONObject){
                //如果为jsonobject 则调用
                obj = getJsonObj(JSONObject.parseObject(String.valueOf(obj)), fieldRulsJson, null);
                jsonArrs.add(obj);
            }else if(obj instanceof JSONArray){
                obj = getJsonArr(JSONArray.parseArray(String.valueOf(obj)), fieldRulsJson, null);
                jsonArrs.add(obj);
            }else if(obj instanceof String){

            }
        }
        jsonObj.put(key,jsonArrs);
        return jsonObj;
    }
}
